package com.dclick.marcos.sound
{
	import flash.events.SampleDataEvent;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	
	import mx.collections.ArrayCollection;
	
	public class NotesPlayer
	{
		public static const A3:Number = 	220;
		public static const Asus3:Number = 	232.980;
		public static const B3:Number = 	246.725820;
		public static const C3:Number = 	261.6255653;
		public static const Csus3:Number = 	276.6983193;
		public static const D3:Number = 	293.0235201;
		public static const Dsus3:Number = 	310.3119078;
		public static const E3:Number = 	328.6203104;
		public static const F3:Number = 	348.0089087;
		public static const Fsus3:Number = 	368.5414343;
		public static const G3:Number = 	390.2853789;
		public static const Gsus3:Number = 	413.3122163;
		public static const A4:Number = 	440;
		
		public static var amplitudeMaxima:Number = 0.10;
		
		private var tones:ArrayCollection = 
			new ArrayCollection(["A","A#","B","C","C#","D","D#","E","F","F#","G","G#"]);
		
		private var soundObject:Sound;
		private var sChannel:SoundChannel;
		
		
		private var frequencia:Number = A3;
		public static var samplesQuantity:Number = 4412;
		
		public function NotesPlayer()
		{
		}
			
		public function startSound(_frequencia:Number=A3, _samplesQuantity:Number=2500):void
		{
			samplesQuantity = _samplesQuantity;
			frequencia = _frequencia;
			
			if(soundObject == null)
			{
				soundObject = new Sound();
			}
			if(sChannel != null)
			{
				sChannel.stop();
			}
			if(soundObject.hasEventListener(SampleDataEvent.SAMPLE_DATA) == false)
			{
				soundObject.addEventListener(SampleDataEvent.SAMPLE_DATA, sineWaveGenerator);
			}
			sChannel = soundObject.play();
		}
		
		
		public function stopSound():void
		{
			if(sChannel)
			{
				sChannel.stop();
			}
		}
		
		public function setSamplesQuantity(value:int):void
		{
			//Provide between 2048 and 8192 samples in a SampleDataEvent object
			if(value >= 2048 && value <= 8192)
			{
				samplesQuantity = value;
			}
		}
		
		private function sineWaveGenerator(event:SampleDataEvent):void
		{
		    for (var c:Number=0; c<samplesQuantity; c++)
		    {
				var frequenciaModular:Number = frequencia/44100;
				var sample:Number = amplitudeMaxima*Math.sin(frequenciaModular*Number(c+event.position)*Math.PI*2);
				
		    	event.data.writeFloat(sample);
		    	event.data.writeFloat(sample);
			}
		}
		
		public function getNoteFrequency(tone:String, scale:Number):Number
		{
			tone = tone.toUpperCase();
			if(tones.contains(tone) == false)
			{
				throw new Error("The tone \""+tone+"\" specifyed doesn't exists.");
			}
			else
			{
				// 3 é a escala de número 3 (C3)
				var scaleDistance:int = scale - 3;
				var tonePositionOnScale:int = tones.getItemIndex(tone)+1;
				
				// 3 é a posição do C(Dó) na escala padrão inciada em A(Lá Tônico)
				var currentScaleHalfTonesDiff:int = tonePositionOnScale - 3;
				
				var distanceInHalfTones:int;
				
				if(scaleDistance < 0)
				{
					distanceInHalfTones = scaleDistance*(-12)-(12-currentScaleHalfTonesDiff);
				}
				else if(scaleDistance == 0)
				{
					distanceInHalfTones = currentScaleHalfTonesDiff;
				}
				else
				{
					distanceInHalfTones = scaleDistance*12-(12-currentScaleHalfTonesDiff);
				}
				
				var freq:Number = calculateFrequecy(distanceInHalfTones);
				
				return freq;
			}
		}
		
		////////////////////////////////////////////////////////////////////////////////////
		private function calculateFrequecy(halfToneDistance:int, baseNote:Number=C3):Number
		{
			//f = fo*k^n
			return Number(baseNote * Math.pow(1059, halfToneDistance));
		}
		
		public function testCalculateFrequency():String
		{
			var str:String = "";
			
			var freq:Number;
			var note:String;
			var semitons:int = 1;
			
			for each(var n:String in tones)
			{
				if(n != "A")
				{
					note = n;
					freq = calculateFrequecy(semitons, 220);
					str += "\n"+note+"="+freq.toString();
					semitons++;
				}
			}
			
			return str;
		}
		
		
	}
}